var FC = $.fullCalendar; // a reference to FullCalendar's root namespace
var View = FC.View; // the class that all views must inherit from
var BasicView = FC.BasicView;
var MonthView = FC.MonthView; // the class that all views must inherit from
var DayGrid = FC.DayGrid;
var Scroller = FC.Scroller;
var DayGridForYear, YearView; // our subclass

/**
 * 重定义DayGrid
 */
DayGridForYear = DayGrid.extend({

    month : null,
    range : null,

    // Tells the grid about what period of time to display.
    // Any date-related internal data should be generated.
    setRange : function(range) {
        this.range = range;
        this.month = this.range.intervalStart.month();
        this.start = range.start.clone();
        this.end = range.end.clone();
        this.rangeUpdated();
        this.processRangeOptions();
    },

    // 表头渲染
    renderHeadTrHtml : function() {
        var monthTitleFormat = this.view.opt('monthTitleFormat');
        monthTitleFormat = monthTitleFormat ? monthTitleFormat : 'MMM';
        var isNeedMonthTitle = !(this.view.monthBgn == this.view.monthEnd);

        return ((monthTitleFormat && isNeedMonthTitle) ? '' + '<tr><th colspan="7">' + this.range.intervalStart.format(monthTitleFormat) + '</th></tr>' : '') + DayGrid.prototype.renderHeadTrHtml.call(this);
    },

    // Generates the HTML that will go before the day-of week header cells
    renderHeadIntroHtml : function() {
        var view = this.view;

        if (view.weekNumbersVisible) {
            return '' + '<th class="fc-week-number ' + view.widgetHeaderClass + '" ' + view.weekNumberStyleAttr() + '>' + '<span>' + htmlEscape(view.opt('weekNumberTitle')) + '</span>' + '</th>';
        }

        return '';
    },

    // Generates the HTML that will go before content-skeleton cells that
    // display the day/week numbers
    renderNumberIntroHtml : function(row) {
        var view = this.view;

        if (view.weekNumbersVisible) {
            return '' + '<td class="fc-week-number" ' + view.weekNumberStyleAttr() + '>' + '<span>' + this.getCellDate(row, 0).format('w') + '</span>' + '</td>';
        }

        return '';
    },

    // Generates the HTML that goes before the day bg cells for each day-row
    renderBgIntroHtml : function() {
        var view = this.view;

        if (view.weekNumbersVisible) {
            return '<td class="fc-week-number ' + view.widgetContentClass + '" ' + view.weekNumberStyleAttr() + '></td>';
        }

        return '';
    },

    // Generates the HTML that goes before every other type of row generated by
    // DayGrid.
    // Affects helper-skeleton and highlight-skeleton rows.
    renderIntroHtml : function() {
        var view = this.view;

        if (view.weekNumbersVisible) {
            return '<td class="fc-week-number" ' + view.weekNumberStyleAttr() + '></td>';
        }

        return '';
    },

    renderBgCellHtml : function(date, otherAttrs) {
        var view = this.view;
        var classes = this.getDayClasses(date);

        classes.unshift('fc-day', view.widgetContentClass);

        if (view.dayNumbersVisibleInBg) {
            classes.unshift("fc-bg-day-number");
        }

        return '<td class="' + classes.join(' ') + '"' + ' data-date="' + date.format('YYYY-MM-DD') + '"' + (otherAttrs ? ' ' + otherAttrs : '') + '>' + (view.dayNumbersVisibleInBg ? date.date() : '') + '</td>';
    },

    // Builds the HTML needed for one fill segment. Generic enought o work with
    // different types.
    fillSegHtml : function(type, seg) {
        var el = DayGrid.prototype.fillSegHtml.call(this, type, seg);
        if (seg && seg.event && seg.event.faClassName) {
            var $el = $(el);
            var faClassesStr = seg.event.faClassName;
            var faClasses = faClassesStr.split(",");

            if (faClasses.length > 0) {
                $el.addClass("fc-bg-fa");
                $el.append('<span class="fa-stack fa-lg" />');
                var faStackSpan = $el.find("span.fa-stack");
                for (var i = 0; i < faClasses.length; i++) {
                    var faClass = faClasses[i];
                    faStackSpan.append('<i class="fa ' + faClass + ' fa-stack-1x" ></i>');
                }
            }
            return $el.prop("outerHTML");
        }

        return el;
    },

    renderFillRow : function(type, seg, className) {
        var colCnt = this.colCnt;
        var startCol = seg.leftCol;
        var endCol = seg.rightCol + 1;
        var skeletonEl;
        var trEl;

        className = className || type.toLowerCase();

        skeletonEl = $('<div class="fc-' + className + '-skeleton">' + '<table><tr/></table>' + '</div>');
        trEl = skeletonEl.find('tr');

        if (startCol > 0) {
            trEl.append('<td colspan="' + startCol + '"/>');
        }

        var newEl = seg.el.clone();
        var baseMoment = seg.event.start.clone();
        if (!baseMoment.isSame(this.range.intervalStart, "month")) {
            seg.el.addClass("fc-other-month");
            if (!this.view.otherMonthEventVisible) {
                seg.el.find("span.fa-stack").hide();
            }
        }
        for (var i = 1; i < (endCol - startCol); i++) {debugger
            baseMoment.add(1, 'd');
            var itemEl = newEl.clone();
            if (!baseMoment.isSame(this.range.intervalStart, "month")) {
                itemEl.addClass("fc-other-month");
                if (!this.view.otherMonthEventVisible) {
                    itemEl.find("span.fa-stack").hide();
                }
            }
            seg.el = seg.el.add(itemEl);
        }
        trEl.append(seg.el);

        if (endCol < colCnt) {
            trEl.append('<td colspan="' + (colCnt - endCol) + '"/>');
        }

        this.bookendCells(trEl);

        return skeletonEl;
    },

    // Computes HTML classNames for a single-day element
    getDayClasses : function(date) {
        var viewIntervalStart = this.view.intervalStart;
        this.view.intervalStart = this.range.intervalStart;
        var classes = DayGrid.prototype.getDayClasses.call(this, date);
        if (this.view.intervalDuration.as('months') != 1 && date.month() != this.month) {
            classes.push('fc-other-month');
        }
        this.view.intervalStart = viewIntervalStart;

        return classes;
    }
});

/**
 * 定义年视图
 */
YearView = MonthView.extend({ // make a subclass of View

    baseMomentDate : null,
    baseYear : null,
    baseMonth : null, // [0-11]
    regionLength : null,
    monthBgn : null,
    monthEnd : null,
    baseDate : null,
    displayMonthCount : 12,
    dayGrids : null,
    dayNumbersVisibleInBg : false, // 居中渲染日期数值
    otherMonthEventVisible : false, // 是否显示其他月事件

    initialize : function() {
        if (this.opt("displayMonthCount")) {
            this.displayMonthCount = this.opt("displayMonthCount");
        }

        if (this.opt("otherMonthEventVisible")) {
            this.otherMonthEventVisible = true;
        }

        // 缓存12个月的数据
        this.dayGrids = [];
        for (var i = 12; i > 0; i--) {
            this.dayGrids.push(new DayGridForYear(this));
        }

        this.scroller = new Scroller({
            overflowX : 'hidden',
            overflowY : 'auto'
        });
    },

    // Sets the view's title property to the most updated computed value
    updateTitle : function() {
        var bgnMonth = moment(this.baseYear + "" + (this.monthBgn + 1), "YYYYMM").locale(this.opt("lang"));
        var endMonth = moment(this.baseYear + "" + (this.monthEnd + 1), "YYYYMM").locale(this.opt("lang"));
        var titleFormat = this.opt('titleFormat');
        titleFormat = titleFormat ? titleFormat : 'LL';
        if (this.monthBgn == this.monthEnd) {
            this.title = bgnMonth.format(titleFormat);
        } else {
            this.title = bgnMonth.format(titleFormat) + " - " + endMonth.format(titleFormat);
        }
    },

    // render the day-of-week headers
    renderHead : function() {
        for (var i = this.monthBgn; i <= this.monthEnd; i++) {
            var headContainerClass = '.fc-year-month-' + i;
            this.el.find(headContainerClass + ' .fc-head-container').html(this.dayGrids[i].renderHeadHtml());
        }

        this.headContainerEl = this.el.find('.fc-head-container');
        this.headRowEl = this.headContainerEl.find('.fc-row');
    },

    // Builds the HTML skeleton for the view.
    // The day-grid and time-grid components will render inside containers
    // defined by this HTML.
    renderSkeletonHtml : function() {
        var html = MonthView.prototype.renderSkeletonHtml.call(this);

        var retHtml = "";
        for (var i = this.monthBgn; i <= this.monthEnd; i++) {
            retHtml += html.replace(/<table>/, "<table class='" + (i == this.baseMonth ? "fc-year-month-curr" : "") + " fc-year-month-" + i + "' date-month='" + (this.baseYear + "-" + (i + 1)) + "'>");
        }

        return retHtml;
    },

    // Renders the view into `this.el`, which should already be assigned
    renderDates : function() {
        this.dayNumbersVisibleInBg = this.opt('dayNumbersVisibleInBg');
        this.dayNumbersVisible = !this.dayNumbersVisibleInBg && this.dayGrid.rowCnt > 1; // TODO:
        // make
        // grid
        // responsible
        this.weekNumbersVisible = this.opt('weekNumbers');

        this.el.addClass('fc-basic-view').html(this.renderSkeletonHtml());
        this.renderHead();

        this.scroller.render();
        var dayGridContainerEl = this.scroller.el.addClass('fc-day-grid-container');
        var dayGridEl = $('<div class="fc-day-grid" />').appendTo(dayGridContainerEl);
        this.el.find('.fc-body > tr > td').append(dayGridContainerEl);

        var grid = null;
        for (var i = this.monthBgn; i <= this.monthEnd; i++) {
            grid = this.dayGrids[i];
            dayGridEl = this.el.find('.fc-year-month-' + i + ' > .fc-body > tr > td > .fc-day-grid-container > div.fc-day-grid');
            grid.numbersVisible = this.dayNumbersVisible || this.weekNumbersVisible;
            grid.setElement(dayGridEl);
            grid.renderDates(this.hasRigidRows());
        }
    },

    // Updates all internal dates to center around the given current unzoned
    // date.
    setDate : function(date) {
        this.baseMomentDate = date;
        var regionLength = this.displayMonthCount;
        this.baseMonth = date.month(); // [0-11]
        this.baseYear = date.year();
        this.monthBgn = Math.floor(this.baseMonth / regionLength) * regionLength;
        this.monthEnd = (Math.floor(this.baseMonth / regionLength) + 1) * regionLength - 1;
        this.baseDate = date.date(); // [0-29]
        this.otherMonthEventVisible = this.opt("otherMonthEventVisible") ? true : false;
        this.dayGrid = this.dayGrids[this.baseMonth];
        this.setRange(BasicView.prototype.computeRange.call(this, date));
    },

    // Sets the display range and computes all necessary dates
    setRange : function(range) {
        MonthView.prototype.setRange.call(this, range); // call the super-method

        var startDate = this.baseMomentDate.clone().subtract(this.baseMonth, "month");
        var tempIntervalDuration = this.intervalDuration;
        this.intervalDuration = moment.duration(1, 'month');
        for (var i = this.monthBgn; i <= this.monthEnd; i++) {
            this.dayGrids[i].breakOnWeeks = this.dayGrid.breakOnWeeks;
            this.dayGrids[i].setRange(this.computeRange(startDate.clone().add(i, "month")));
        }
        this.intervalDuration = tempIntervalDuration;
    },

    renderBusinessHours : function() {
        this.applyForDayGrids(MonthView.prototype.renderBusinessHours, this, arguments);
    },

    // Overrides the default BasicView behavior to have special multi-week
    // auto-height logic
    setHeight : function(totalHeight, isAuto) {
        this.applyForDayGrids(MonthView.prototype.setHeight, this, arguments);
    },

    applyComm : function(func, _this, args) {
        if (typeof func === "function") {
            return func.apply(_this, args);
        }
    },

    applyForDayGrids : function(func, _this, args) {
        var ret;
        for (var i = this.monthBgn; i <= this.monthEnd; i++) {
            this.dayGrid = this.dayGrids[i];
            ret = ret || this.applyComm(func, this, args);
        }
        this.dayGrid = this.dayGrids[this.baseMonth];
        return ret;
    },

    simpleRepeatForDayGrids : function(funcName, args) {
        var ret;
        for (var i = this.monthBgn; i <= this.monthEnd; i++) {
            ret = ret || this.applyComm(this.dayGrids[i][funcName], this.dayGrids[i], args);
        }
        return ret;
    },

	/*
	 * Hit Areas
	 * ------------------------------------------------------------------------------------------------------------------
	 */
    // forward all hit-related method calls to dayGrid
    prepareHits : function() {
        this.simpleRepeatForDayGrids("prepareHits", arguments);
    },

    releaseHits : function() {
        this.simpleRepeatForDayGrids("releaseHits", arguments);
    },

    queryHit : function(left, top) {
        var obj = this.simpleRepeatForDayGrids("queryHit", arguments);
        if (obj && obj.component && obj.component.month >= 0) {
            this.hitMonth = obj.component.month;
        }
        return obj;
    },

    getHitSpan : function(hit) {
        return this.simpleRepeatForDayGrids("getHitSpan", arguments);
    },

    getHitEl : function(hit) {
        return this.simpleRepeatForDayGrids("getHitEl", arguments);
    },

	/*
	 * Events
	 * ------------------------------------------------------------------------------------------------------------------
	 */

    // Renders the given events onto the view and populates the segments array
    renderEvents : function(events) {
        this.simpleRepeatForDayGrids("renderEvents", arguments);
        this.updateHeight(); // must compensate for events that overflow the
        // row
    },

    // Retrieves all segment objects that are rendered in the view
    getEventSegs : function() {
        return this.simpleRepeatForDayGrids("getEventSegs", arguments);
    },

    // Unrenders all event elements and clears internal segment data
    unrenderEvents : function() {
        this.simpleRepeatForDayGrids("unrenderEvents", arguments);

        // we DON'T need to call updateHeight() because:
        // A) a renderEvents() call always happens after this, which will
        // eventually call updateHeight()
        // B) in IE8, this causes a flash whenever events are rerendered
    },

	/*
	 * Dragging (for both events and external elements)
	 * ------------------------------------------------------------------------------------------------------------------
	 */

    // A returned value of `true` signals that a mock "helper" event has been
    // rendered.
    renderDrag : function(dropLocation, seg) {
        this.simpleRepeatForDayGrids("renderDrag", arguments);

        return;
    },

    unrenderDrag : function() {
        this.simpleRepeatForDayGrids("unrenderDrag", arguments);
    },

	/*
	 * Selection
	 * ------------------------------------------------------------------------------------------------------------------
	 */

    // Renders a visual indication of a selection
    renderSelection : function(span) {
        this.simpleRepeatForDayGrids("renderSelection", arguments);
    },

    // Unrenders a visual indications of a selection
    unrenderSelection : function() {
        this.simpleRepeatForDayGrids("unrenderSelection", arguments);
    }
});

FC.views.year = {
    'class' : YearView,
    duration : {
        years : 1
    }, // important for prev/next
    defaults : {
        fixedWeekCount : true,
        displayMonthCount : 12,
        defaultView : "year"
    }
};

FC.views.halfyear = {
    'class' : YearView,
    duration : {
        years : 0.5
    }, // important for prev/next
    defaults : {
        fixedWeekCount : true,
        displayMonthCount : 6,
        defaultView : "halfyear"
    }
};

FC.views.monthyear = {
    'class' : YearView,
    duration : {
        months : 1
    }, // important for prev/next
    defaults : {
        fixedWeekCount : true,
        displayMonthCount : 1,
        otherMonthEventVisible : true,
        defaultView : "monthyear"
    }
};

/**
 * 增加年视图中文说明
 */
FC.lang("zh-cn", {
    buttonText : {
        year : "年",
        halfyear : "半年",
        monthyear : "月"
    }
});